import { IEnumerator } from "./IEnumerator"
const EPSILON: number = 0.00001;
const PiBy180: number = 0.017453292519943295;
export class vec2 {
  public values: Float32Array;

  public constructor(x: number = 0, y: number = 0) {
    this.values = new Float32Array([x, y]);
  }

  public toString(): string {
    return " [ " + this.values[0] + " , " + this.values[1] + " ] ";
  }

  public get x(): number { return this.values[0]; }
  public set x(x: number) { this.values[0] = x; }

  public get y(): number { return this.values[1]; }
  public set y(y: number) { this.values[1] = y; }

  public reset(x: number = 0, y: number = 0): vec2 {
    this.values[0] = x;
    this.values[1] = y;
    return this;
  }

  public equals(vector: vec2): boolean {
    if (Math.abs(this.values[0] - vector.values[0]) > EPSILON)
      return false;

    if (Math.abs(this.values[1] - vector.values[1]) > EPSILON)
      return false;

    return true;
  }

  public negative(): vec2 {
    this.values[0] = - this.values[0];
    this.values[1] = - this.values[1];
    return this;
  }

  public get squaredLength(): number {
    let x = this.values[0];
    let y = this.values[1];
    return (x * x + y * y);
  }

  public get length(): number {
    return Math.sqrt(this.squaredLength);
  }

  public normalize(): number {
    let len: number = this.length;
    if (Math2D.isEquals(len, 0)) {
      console.log(" the length = 0 ");
      this.values[0] = 0;
      this.values[1] = 0;
      return 0;
    }

    if (Math2D.isEquals(len, 1)) {
      console.log(" the length = 1 ");
      return 1.0;
    }

    this.values[0] /= len;
    this.values[1] /= len;
    return len;
  }

  public static create(x: number = 0, y: number = 0): vec2 {
    return new vec2(x, y);
  }

  public add(right: vec2): vec2 {
    vec2.sum(this, right, this);
    return this;
  }

  public static sum(left: vec2, right: vec2, result: vec2 | null = null): vec2 {
    if (result === null) result = new vec2();
    result.values[0] = left.values[0] + right.values[0];
    result.values[1] = left.values[1] + right.values[1];
    return result;
  }

  public substract(another: vec2): vec2 {
    vec2.difference(this, another, this);
    return this;
  }

  public static difference(end: vec2, start: vec2, result: vec2 | null = null): vec2 {
    if (result === null) result = new vec2();
    result.values[0] = end.values[0] - start.values[0];
    result.values[1] = end.values[1] - start.values[1];
    return result;
  }

  public static copy(src: vec2, result: vec2 | null = null): vec2 {
    if (result === null) result = new vec2();
    result.values[0] = src.values[0];
    result.values[1] = src.values[1];
    return result;
  }

  public static scale(direction: vec2, scalar: number, result: vec2 | null = null): vec2 {
    if (result === null) result = new vec2();
    result.values[0] = direction.values[0] * scalar;
    result.values[1] = direction.values[1] * scalar;
    return result;
  }

  // 沿direction方向移动scalar的距离
  public static scaleAdd(start: vec2, direction: vec2, scalar: number, result: vec2 | null = null): vec2 {
    if (result === null) result = new vec2();
    vec2.scale(direction, scalar, result);
    return vec2.sum(start, result, result);
  }

  public static moveTowards(start: vec2, direction: vec2, scalar: number, result: vec2 | null = null): vec2 {
    if (result === null) result = new vec2();
    vec2.scale(direction, scalar, result);
    return vec2.sum(start, result, result);
  }

  public innerProduct(right: vec2): number {
    return vec2.dotProduct(this, right);
  }

  // 点乘
  public static dotProduct(left: vec2, right: vec2): number {
    return left.values[0] * right.values[0] + left.values[1] * right.values[1];
  }

  // 叉乘
  public static crossProduct(left: vec2, right: vec2): number {
    return left.x * right.y - left.y * right.x;
  }

  // 以点from作为原点，获得点to的方向（相对于x轴正方向）
  public static getOrientation(from: vec2, to: vec2, isRadian: boolean = false): number {
    let diff: vec2 = vec2.difference(to, from);
    let radian = Math.atan2(diff.y, diff.x);
    if (isRadian === false) {
      radian = Math2D.toDegree(radian);
    }
    return radian;
  }

  // 获得方向a到方向b的夹角的弧度或角度
  public static getAngle(a: vec2, b: vec2, isRadian: boolean = false): number {
    let dot: number = vec2.dotProduct(a, b);
    let radian: number = Math.acos(dot / (a.length * b.length));
    if (isRadian === false) {
      radian = Math2D.toDegree(radian);
    }
    return radian;
  }

  // 获取方向a到方向b的夹角的余弦值
  public static cosAngle(a: vec2, b: vec2, norm: boolean = false): number {
    if (norm === true) {
      a.normalize();
      b.normalize();
    }
    return vec2.dotProduct(a, b);
  }

  // 获取方向a到方向b的夹角的正弦值
  public static sinAngle(a: vec2, b: vec2, norm: boolean = false): number {
    if (norm === true) {
      a.normalize();
      b.normalize();
    }
    return (a.x * b.y - b.x * a.y);
  }

  public static zero = new vec2(0, 0);
  public static xAxis = new vec2(1, 0);
  public static yAxis = new vec2(0, 1);
  public static nXAxis = new vec2(- 1, 0);
  public static nYAxis = new vec2(0, - 1);
  public static temp = new vec2(0, 0);
  public static temp1 = new vec2(0, 0);

}

export class vec3 {
  public values: Float32Array;
  public constructor(x: number = 0, y: number = 0, z: number = 0) {
    this.values = new Float32Array([x, y, z]);
  }

  public get x(): number { return this.values[0]; }
  public set x(x: number) { this.values[0] = x; }

  public get y(): number { return this.values[1]; }
  public set y(y: number) { this.values[1] = y; }

  public get z(): number { return this.values[2]; }
  public set z(z: number) { this.values[2] = z; }

  public static cross(v1: vec3, v2: vec3, out: vec3 | null = null): vec3 {
    if (out === null) out = new vec3();
    out.x = v1.y * v2.z - v1.z * v2.y;
    out.y = v1.z * v2.x - v1.x * v2.z;
    out.z = v1.x * v2.y - v1.y * v2.x;
    return out;
  }

  public toString(): string {
    return " [ " + this.values[0] + " , " + this.values[1] + " , " + this.values[2] + " ] ";
  }
}

export class mat2d {
  public values: Float32Array;

  public constructor(a: number = 1, b: number = 0, c: number = 0, d: number = 1, x: number = 0, y: number = 0) {
    this.values = new Float32Array([a, b, c, d, x, y]);
  }

  public identity(): void {
    this.values[0] = 1.0;
    this.values[1] = 0.0;
    this.values[2] = 0.0;
    this.values[3] = 1.0;
    this.values[4] = 0.0;
    this.values[5] = 0.0;
  }

  public static create(a: number = 1, b: number = 0, c: number = 0, d: number = 1, x: number = 0, y: number = 0): mat2d {
    return new mat2d(a, b, c, d, x, y);
  }

  public get xAxis(): vec2 {
    return vec2.create(this.values[0], this.values[1]);
  }

  public get yAxis(): vec2 {
    return vec2.create(this.values[2], this.values[3]);
  }

  public get origin(): vec2 {
    return vec2.create(this.values[4], this.values[5])
  }

  public getAngle(isRadian: boolean = false): number {
    let angle: number = Math.atan2(this.values[1], this.values[0]);
    if (isRadian) {
      return angle;
    }
    return angle / PiBy180;
  }

  public static copy(src: mat2d, result: mat2d | null = null): mat2d {
    if (result === null) result = new mat2d();
    result.values[0] = src.values[0];
    result.values[1] = src.values[1];
    result.values[2] = src.values[2];
    result.values[3] = src.values[3];
    result.values[4] = src.values[4];
    result.values[5] = src.values[5];
    return result;
  }


  public static multiply(left: mat2d, right: mat2d, result: mat2d | null = null): mat2d {
    if (result === null) result = new mat2d();

    let a0: number = left.values[0];
    let a1: number = left.values[1];
    let a2: number = left.values[2];
    let a3: number = left.values[3];
    let a4: number = left.values[4];
    let a5: number = left.values[5];

    let b0: number = right.values[0];
    let b1: number = right.values[1];
    let b2: number = right.values[2];
    let b3: number = right.values[3];
    let b4: number = right.values[4];
    let b5: number = right.values[5];

    result.values[0] = a0 * b0 + a2 * b1;
    result.values[1] = a1 * b0 + a3 * b1;
    result.values[2] = a0 * b2 + a2 * b3;
    result.values[3] = a1 * b2 + a3 * b3;
    result.values[4] = a0 * b4 + a2 * b5 + a4;
    result.values[5] = a1 * b4 + a3 * b5 + a5;

    return result;
  }

  public static determinant(mat: mat2d): number {
    return mat.values[0] * mat.values[3] - mat.values[2] * mat.values[1];
  }

  public static invert(src: mat2d, result: mat2d): boolean {
    let det: number = mat2d.determinant(src);

    if (Math2D.isEquals(det, 0)) {
      return false;
    }

    det = 1.0 / det;

    result.values[0] = src.values[3] * det;
    result.values[1] = - src.values[1] * det;
    result.values[2] = - src.values[2] * det;
    result.values[3] = src.values[0] * det;
    result.values[4] = (src.values[2] * src.values[5] - src.values[3] * src.values[4]) * det;
    result.values[5] = (src.values[1] * src.values[4] - src.values[0] * src.values[5]) * det;
    return true;
  }

  public static makeRotation(radians: number, result: mat2d | null = null): mat2d {
    if (result === null) result = new mat2d();
    let s: number = Math.sin(radians), c: number = Math.cos(radians);
    result.values[0] = c;
    result.values[1] = s;
    result.values[2] = -s;
    result.values[3] = c;
    result.values[4] = 0;
    result.values[5] = 0;
    return result;
  }

  // 旋转矩阵取逆（当前矩阵只能包含旋转信息）
  public onlyRotationMatrixInvert(): mat2d {
    let s: number = this.values[1];
    this.values[1] = this.values[2];
    this.values[2] = s;
    return this;
  }

  // 构建从方向v1到方向v2的旋转矩阵（例如获取x轴正方的向量到某个向量的旋转矩阵，这样可以代替Math.atan2的一些应用）
  public static makeRotationFromVectors(v1: vec2, v2: vec2, norm: boolean = false, result: mat2d | null = null): mat2d {
    if (result === null) result = new mat2d();
    result.values[0] = vec2.cosAngle(v1, v2, norm);
    result.values[1] = vec2.sinAngle(v1, v2, norm);
    result.values[2] = - vec2.sinAngle(v1, v2, norm);
    result.values[3] = vec2.cosAngle(v1, v2, norm);
    result.values[4] = 0;
    result.values[5] = 0;
    return result;
  }

  public static makeReflection(axis: vec2, result: mat2d | null = null): mat2d {
    if (result === null) result = new mat2d();
    result.values[0] = 1 - 2 * axis.x * axis.x;
    result.values[1] = - 2 * axis.x * axis.y;
    result.values[2] = - 2 * axis.x * axis.y;
    result.values[3] = 1 - 2 * axis.y * axis.y;
    result.values[4] = 0;
    result.values[5] = 0;
    return result;
  }

  public static makeXSkew(sx: number, result: mat2d | null = null): mat2d {
    if (result === null) result = new mat2d();
    result.values[0] = 1;
    result.values[1] = 0;
    result.values[2] = sx;
    result.values[3] = 1;
    result.values[4] = 0;
    result.values[5] = 0;
    return result;
  }

  public static makeYSkew(sy: number, result: mat2d | null = null): mat2d {
    if (result === null) result = new mat2d();
    result.values[0] = 1;
    result.values[1] = sy;
    result.values[2] = 0;
    result.values[3] = 1;
    result.values[4] = 0;
    result.values[5] = 0;
    return result;
  }

  public static makeTranslation(tx: number, ty: number, result: mat2d | null = null): mat2d {
    if (result === null) result = new mat2d();
    result.values[0] = 1;
    result.values[1] = 0;
    result.values[2] = 0;
    result.values[3] = 1;

    result.values[4] = tx;
    result.values[5] = ty;
    return result;
  }

  public static makeScale(sx: number, sy: number, result: mat2d | null = null): mat2d {
    if (Math2D.isEquals(sx, 0) || Math2D.isEquals(sy, 0)) {
      alert(" x轴或y轴缩放系数为0 ");
      throw new Error(" x轴或y轴缩放系数为0 ");
    }

    if (result === null) result = new mat2d();
    result.values[0] = sx;
    result.values[1] = 0;
    result.values[2] = 0;
    result.values[3] = sy;
    result.values[4] = 0;
    result.values[5] = 0;
    return result;
  }

  public static temp1 = mat2d.create();
  public static temp2 = mat2d.create();
  public static quadBezierBasicMatrix = mat2d.create(1, -2, -2, 2, 1, 0);
}

export class MatrixStack {
  private _mats: mat2d[];
  public constructor() {
    this._mats = [];
    this._mats.push(new mat2d());
  }

  public get matrix(): mat2d {
    if (this._mats.length === 0) {
      alert(" 矩阵堆栈为空 ");
      throw new Error(" 矩阵堆栈为空 ");
    }

    return this._mats[this._mats.length - 1];
  }

  public pushMatrix(): void {
    let mat: mat2d = mat2d.copy(this.matrix);
    this._mats.push(mat);
  }

  public popMatrix(): void {
    if (this._mats.length === 0) {
      alert(" 矩阵堆栈为空 ");
      return;
    }
    this._mats.pop();
  }

  public loadIdentity(): void {
    this.matrix.identity();
  }

  public loadMatrix(mat: mat2d): void {
    mat2d.copy(mat, this.matrix);
  }

  public multMatrix(mat: mat2d): void {
    mat2d.multiply(this.matrix, mat, this.matrix);
  }

  public translate(x: number = 0, y: number = 0): void {
    let mat: mat2d = mat2d.makeTranslation(x, y);
    this.multMatrix(mat);
  }

  public rotate(degree: number = 0, isRadian: boolean = false): void {
    if (isRadian === false) {
      degree = Math2D.toRadian(degree);
    }
    let mat: mat2d = mat2d.makeRotation(degree);
    this.multMatrix(mat);
  }

  public rotateFrom(v1: vec2, v2: vec2, norm: boolean = false): void {
    let mat: mat2d = mat2d.makeRotationFromVectors(v1, v2, norm);
    this.multMatrix(mat);
  }

  public scale(x: number = 1.0, y: number = 1.0): void {
    let mat: mat2d = mat2d.makeScale(x, y);
    this.multMatrix(mat);
  }

  public invert(): mat2d {
    let ret: mat2d = new mat2d();
    if (mat2d.invert(this.matrix, ret) === false) {
      alert(" 堆栈顶部矩阵为奇异矩阵，无法求逆 ");
      throw new Error(" 堆栈顶部矩阵为奇异矩阵，无法求逆 ");
    }
    return ret;
  }
}
export class Math2D {
  public static toRadian(degree: number): number {
    return degree * PiBy180;
  }

  public static toDegree(radian: number): number {
    return radian / PiBy180;
  }

  public static random(from: number, to: number): number {
    return Math.random() * to + from;
  }

  public static angleSubtract(from: number, to: number): number {
    let diff: number = to - from;
    while (diff > 180) {
      diff -= 360;
    }

    while (diff < - 180) {
      diff += 360;
    }

    return diff;
  }

  public static isEquals(left: number, right: number, espilon: number = EPSILON): boolean {
    if (Math.abs(left - right) >= EPSILON) {
      return false;
    }
    return true;
  }

  public static getQuadraticBezierPosition(start: number, ctrl: number, end: number, t: number): number {
    if (t < 0.0 || t > 1.0) {
      alert(" t的取值范围必须为[ 0 , 1 ] ");
      throw new Error(" t的取值范围必须为[ 0 , 1 ] ");
    }
    let t1: number = 1.0 - t;
    let t2: number = t1 * t1;
    return t2 * start + 2.0 * t * t1 * ctrl + t * t * end;
  }

  public static getQuadraticBezierVector(start: vec2, ctrl: vec2, end: vec2, t: number, result: vec2 | null = null): vec2 {
    if (result === null) result = vec2.create();
    result.x = Math2D.getQuadraticBezierPosition(start.x, ctrl.x, end.x, t);
    result.y = Math2D.getQuadraticBezierPosition(start.y, ctrl.y, end.y, t);
    return result;
  }

  public static getQuadraticBezierMat(start: vec2, ctrl: vec2, end: vec2, t: number, result: vec2 | null = null): vec2 {
    if (result === null) result = vec2.create();

    return result;
  }

  public static getCubicBezierPosition(start: number, ctrl0: number, ctrl1: number, end: number, t: number): number {
    if (t < 0.0 || t > 1.0) {
      alert(" t的取值范围必须为[ 0 , 1 ] ");
      throw new Error(" t的取值范围必须为[ 0 , 1 ] ");
    }
    let t1: number = (1.0 - t);
    let t2: number = t * t;
    let t3: number = t2 * t;
    return (t1 * t1 * t1) * start + 3 * t * (t1 * t1) * ctrl0 + (3 * t2 * t1) * ctrl1 + t3 * end;
  }

  public static getCubicBezierVector(start: vec2, ctrl0: vec2, ctrl1: vec2, end: vec2, t: number, result: vec2 | null = null): vec2 {
    if (result === null) result = vec2.create();
    result.x = Math2D.getCubicBezierPosition(start.x, ctrl0.x, ctrl1.x, end.x, t);
    result.y = Math2D.getCubicBezierPosition(start.y, ctrl0.y, ctrl1.y, end.y, t);
    return result;
  }

  public static createQuadraticBezierEnumerator(start: vec2, ctrl: vec2, end: vec2, steps: number = 30): IBezierEnumerator {
    return new BezierEnumerator(start, end, ctrl, null, steps);
  }

  public static createCubicBezierEnumerator(start: vec2, ctrl0: vec2, ctrl1: vec2, end: vec2, steps: number = 30): IBezierEnumerator {
    return new BezierEnumerator(start, end, ctrl0, ctrl1, steps);
  }

  public static projectPointOnLineSegment(pt: vec2, start: vec2, end: vec2, closePoint: vec2): boolean {
    let v0: vec2 = vec2.create();
    let v1: vec2 = vec2.create();
    let d: number = 0;

    vec2.difference(pt, start, v0);
    vec2.difference(end, start, v1);
    d = v1.normalize();

    let t: number = vec2.dotProduct(v0, v1);
    if (t < 0) {
      closePoint.x = start.x;
      closePoint.y = start.y;
      return false;
    } else if (t > d) {
      closePoint.x = end.x;
      closePoint.y = end.y;
      return false;
    } else {
      vec2.scaleAdd(start, v1, t, closePoint);
      return true;
    }
  }

  // 判断点是否在线段上。先判断点在线段上是否存在投影，再判断点和投影点的距离是否小于半径
  public static isPointOnLineSegment(pt: vec2, start: vec2, end: vec2, radius: number = 2): boolean {
    let closePt: vec2 = vec2.create();
    if (Math2D.projectPointOnLineSegment(pt, start, end, closePt) === false) {
      return false;
    }
    return Math2D.isPointInCircle(pt, closePt, radius);
  }

  public static isPointInCircle(pt: vec2, center: vec2, radius: number): boolean {
    let diff: vec2 = vec2.difference(pt, center);
    let len2: number = diff.squaredLength;
    if (len2 <= radius * radius) {
      return true;
    }
    return false;
  }

  public static isPointInRect(ptX: number, ptY: number, x: number, y: number, w: number, h: number): boolean {
    if (ptX >= x && ptX <= x + w && ptY >= y && ptY <= y + h) {
      return true;
    }
    return false;
  }

  // 判断两个矩形是否碰撞
  public static isCollisionWithRect(
    x1: number, y1: number, w1: number, h1: number,
    x2: number, y2: number, w2: number, h2: number
  ): boolean {
    if (x1 >= x2 && x1 >= x2 + w2) {
      return false;
    } else if (x1 <= x2 && x1 + w1 <= x2) {
      return false;
    } else if (y1 >= y2 && y1 >= y2 + h2) {
      return false;
    } else if (y1 <= y2 && y1 + h1 <= y2) {
      return false;
    }
    return true;
  }

  public static isPointInEllipse(ptX: number, ptY: number, centerX: number, centerY: number, radiusX: number, radiusY: number): boolean {
    let diffX = ptX - centerX;
    let diffY = ptY - centerY;
    let n: number = (diffX * diffX) / (radiusX * radiusX) + (diffY * diffY) / (radiusY * radiusY);
    return n <= 1.0;
  }

  public static sign(v0: vec2, v1: vec2, v2: vec2): number {
    let e1: vec2 = vec2.difference(v0, v2);
    let e2: vec2 = vec2.difference(v1, v2);
    return vec2.crossProduct(e1, e2);
  }

  public static isPointInTriangle(pt: vec2, v0: vec2, v1: vec2, v2: vec2): boolean {
    let b1: boolean = Math2D.sign(v0, v1, pt) < 0.0;
    let b2: boolean = Math2D.sign(v1, v2, pt) < 0.0;
    let b3: boolean = Math2D.sign(v2, v0, pt) < 0.0;
    return ((b1 === b2) && (b2 === b3));
  }

  public static isPointInPolygon(pt: vec2, points: vec2[]): boolean {
    if (points.length < 3) {
      return false;
    }
    for (let i: number = 2; i < points.length; i++) {
      if (Math2D.isPointInTriangle(pt, points[0], points[i - 1], points[i])) {
        return true;
      }
    }
    return false;
  }

  public static isConvex(points: vec2[]): boolean {
    let sign: boolean = Math2D.sign(points[0], points[1], points[2]) < 0;
    let j: number, k: number;
    for (let i: number = 1; i < points.length; i++) {
      j = (i + 1) % points.length;
      k = (i + 2) % points.length;
      if (sign !== Math2D.sign(points[i], points[j], points[k]) < 0) {
        return false;
      }
    }
    return true;
  }

  // 把向量通过变换矩阵进行变换
  public static transform(mat: mat2d, pt: vec2, result: vec2 | null = null): vec2 {
    if (result === null) result = vec2.create();
    result.values[0] = mat.values[0] * pt.values[0] + mat.values[2] * pt.values[1] + mat.values[4];
    result.values[1] = mat.values[1] * pt.values[0] + mat.values[3] * pt.values[1] + mat.values[5];
    return result;
  }

  public static matStack: MatrixStack = new MatrixStack();
}

export class Size {

  public values: Float32Array;

  public constructor(w: number = 1, h: number = 1) {
    this.values = new Float32Array([w, h]);
  }

  set width(value: number) { this.values[0] = value; }
  get width(): number { return this.values[0]; }

  set height(value: number) { this.values[1] = value; }
  get height(): number { return this.values[1]; }

  public static create(w: number = 1, h: number = 1): Size {
    return new Size(w, h);
  }
}

export class Rectangle {
  public origin: vec2;
  public size: Size;

  public constructor(orign: vec2 = new vec2(), size: Size = new Size(1, 1)) {
    this.origin = orign;
    this.size = size;
  }

  public isEmpty(): boolean {
    let area: number = this.size.width * this.size.height;
    if (Math2D.isEquals(area, 0) === true) {
      return true;
    } else {
      return false;
    }
  }

  public static create(x: number = 0, y: number = 0, w: number = 1, h: number = 1): Rectangle {
    let origin: vec2 = new vec2(x, y);
    let size: Size = new Size(w, h);
    return new Rectangle(origin, size);
  }
}

export class Inset {
  public values: Float32Array;

  public constructor(l: number = 0, t: number = 0, r: number = 0, b: number = 0) {
    this.values = new Float32Array([l, t, r, b]);
  }

  public get leftMargin(): number {
    return this.values[0];
  }

  public set leftMargin(value: number) {
    this.values[0] = value;
  }

  public get topMargin(): number {
    return this.values[1];
  }

  public set topMargin(value: number) {
    this.values[1] = value;
  }

  public get rightMargin(): number {
    return this.values[2];
  }

  public set rightMargin(value: number) {
    this.values[2] = value;
  }

  public get bottomMargin(): number {
    return this.values[3];
  }

  public set bottomMargin(value: number) {
    this.values[3] = value;
  }
}

export class Transform2D {
  public position: vec2;
  public rotation: number;
  public scale: vec2;

  public constructor(x: number = 0, y: number = 0, rotation: number = 0, scaleX: number = 1, scaleY: number = 1) {
    this.position = new vec2(x, y);
    this.rotation = rotation;
    this.scale = new vec2(scaleX, scaleY);
  }

  // 根据position、rotation、scale设置栈顶的矩阵并返回栈顶矩阵
  public toMatrix(): mat2d {
    Math2D.matStack.loadIdentity(); // 先把栈顶的矩阵归一化
    Math2D.matStack.translate(this.position.x, this.position.y);
    Math2D.matStack.rotate(this.rotation, false);
    Math2D.matStack.scale(this.scale.x, this.scale.y);
    return Math2D.matStack.matrix;
  }

  public toInvMatrix(result: mat2d): boolean {
    let mat: mat2d = this.toMatrix();
    return mat2d.invert(mat, result);
  }
}


export interface IBezierEnumerator extends IEnumerator<vec2> {
  steps: number; // public 总步数
}

export class BezierEnumerator implements IBezierEnumerator {
  private _steps: number; // 总步数
  private _i: number; // 步长
  private _startAnchorPoint: vec2;
  private _endAnchorPoint: vec2;
  private _controlPoint0: vec2;
  private _controlPoint1: vec2 | null;
  private _currentIdx: number; // 当前步索引

  public constructor(start: vec2, end: vec2, control0: vec2, control1: vec2 | null = null, steps: number = 30) {
    this._startAnchorPoint = start;
    this._endAnchorPoint = end;
    this._controlPoint0 = control0;
    if (control1 !== null) {
      this._controlPoint1 = control1;
    } else {
      this._controlPoint1 = null;
    }
    this._steps = steps;
    this._i = 1.0 / (this._steps);
    this._currentIdx = -1;
  }

  public reset(): void {
    this._currentIdx = - 1;
  }

  public get current(): vec2 {
    if (this._controlPoint1 !== null) {
      return Math2D.getCubicBezierVector(this._startAnchorPoint, this._controlPoint0, this._controlPoint1, this._endAnchorPoint, this._currentIdx * this._i);
    } else {
      return Math2D.getQuadraticBezierVector(this._startAnchorPoint, this._controlPoint0, this._endAnchorPoint, this._currentIdx * this._i);
    }
  }

  public moveNext(): boolean {
    this._currentIdx++;
    return this._currentIdx < this._steps;
  }

  public get steps(): number {
    this._i = 1.0 / (this._steps);
    return this._steps;
  }

  public set steps(steps: number) {
    this._steps = steps;
    this.reset();
  }
}

export class QuadraticBezierEnumerator implements IBezierEnumerator {
  private _steps: number;
  private _i !: number;
  private _startAnchorPoint: vec2;
  private _endAnchorPoint: vec2;
  private _controlPoint0: vec2;
  private _currentIdx: number;

  public constructor(start: vec2, end: vec2, control0: vec2, steps: number = 30) {
    this._startAnchorPoint = start;
    this._endAnchorPoint = end;
    this._controlPoint0 = control0;
    this._steps = steps;
    this._i = 1.0 / (this._steps);
    this._currentIdx = -1;
  }

  public reset(): void {
    this._currentIdx = - 1;
  }

  public get current(): vec2 {
    let t: number = this._currentIdx * this._i;
    let ret: vec2 = vec2.create(t * t, t);
    Math2D.transform(mat2d.quadBezierBasicMatrix, ret, ret);
    ret.x = this._startAnchorPoint.x * ret.x + this._controlPoint0.x * ret.y + this._endAnchorPoint.x;
    ret.y = this._startAnchorPoint.y * ret.x + this._controlPoint0.y * ret.y + this._endAnchorPoint.y;
    return ret;
  }

  public moveNext(): boolean {
    this._currentIdx++;
    return this._currentIdx < this._steps;
  }

  public get steps(): number {
    this._i = 1.0 / (this._steps);
    return this._steps;
  }

  public set steps(steps: number) {
    this._steps = steps;
    this.reset();
  }

}